-- Copyright (c) 2025 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at: http://license.coscl.org.cn/MulanPSL2
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.

local skynet        = require 'skynet'
local class         = require 'mc.class'
local log           = require 'mc.logging'
local object_manage = require 'mc.mdb.object_manage'
local reboot        = require 'mc.mdb.micro_component.reboot'
local base_msg      = require 'messages.base'
local spdm_client   = require 'spdm.client'
local spdm_service  = require 'spdm.service'
local spdm_main     = require 'spdm_core'
local spdm_core     = spdm_main.core

local mctp_callbacks = require 'infrastructure.callback.mctp_callbacks'
local responder_collection = require 'domain.responder_collection'
local spdm_auth_service = require 'service.spdm_auth'
local trusted_component_mdb = require 'interface.mdb.trusted_component_mdb'
local certificate_mdb = require 'interface.mdb.certificate_mdb'
local component_integrity_mdb = require 'interface.mdb.component_integrity_mdb'
local measurement_mdb = require 'interface.mdb.measurement_mdb'
local spdm_component_collection = require 'lualib.interface.discovery.spdm_component_collection'

local PATH_COMPONENT_INTEGRITY_FMT = "/bmc/kepler/Chassis/(%w+)/TrustedComponents/([_%w]+)/ComponentIntegrity/(%w+)"

local app = class(spdm_service)

function app:ctor()
    self.spdm_c = spdm_core.new()
end

function app:init()
    log:notice("spdm app init start")
    app.super.init(self)
    self.spdm_c:init()

    self:service_init()

    self:register_signal_listener()

    self:init_discovery()

    self:register_rpc_methods()

    if skynet.getenv('TEST_DATA_DIR') then
        local spdm_mock = require 'spdm_mock'
        self.m_spdm_mock = spdm_mock.new()
        self.m_spdm_mock:mock_for_it(self.spdm_c)
    end

    log:notice("spdm app init completed")
end

function app:init_discovery()
    object_manage.on_add_object(self.bus, function(_, object, position)
        self.m_spdm_component_collection:add_component(object, position)
    end)
end

function app:register_signal_listener()
    spdm_client:OnHardwareInterfacesAdded(function(_, path, _)
        self.m_spdm_component_collection:register_component_path(path)
    end)
end

function app:service_init()
    self.m_responder_collection = responder_collection.new(self.spdm_c)
    self.m_spdm_auth = spdm_auth_service.new(self.m_responder_collection, self.spdm_c)
    self.m_trusted_component_mdb = trusted_component_mdb.new(self.m_responder_collection)
    self.m_certificate_mdb = certificate_mdb.new(self.m_responder_collection)
    self.m_component_integrity_mdb = component_integrity_mdb.new(self.m_responder_collection)
    self.m_measurement_mdb = measurement_mdb.new(self.m_responder_collection)
    self.m_spdm_component_collection = spdm_component_collection.new(self.bus, self.m_responder_collection,
        self.m_spdm_auth)

    mctp_callbacks.register_dbus(self.bus)
end

function app:register_rpc_methods()
    self:register_rpc()
    self:register_excluded_rpc()
end

function app:register_rpc()
    self:ImplComponentIntegrityMeasurementSetSPDMGetSignedMeasurements(function(obj, ctx, measurement_indices, slot_id,
        nonce)
        local chassis_id, mdb_name, integrity_id = string.match(obj.path, PATH_COMPONENT_INTEGRITY_FMT)
        -- dbus将ay数组转换为string，此处需要手动转换为lua数组
        local indices = {string.byte(measurement_indices, 1, -1)}
        return self.m_responder_collection:get_signed_measurement(ctx, tonumber(chassis_id), mdb_name,
            tonumber(integrity_id), indices, slot_id, nonce)
    end)
end

-- 用于注册一些公共类定义但实体不使用的RPC方法，进行屏蔽
function app:register_excluded_rpc()
    self:ImplTrustedComponentCertificateCertificateDeleteCRL(function()
        error(base_msg.ActionNotSupported("Delete CRL"))
    end)
    self:ImplTrustedComponentCertificateCertificateGetCertificateString(function()
        error(base_msg.ActionNotSupported("Get Certificate String"))
    end)
end

function app:init_reboot()
    reboot.on_prepare(function(...)
        self.spdm_c:free()
        log:info("free spdm lib before reboot")
    end)
    reboot.on_action(function(...)
        log:info("spdm has no extra action for reboot")
    end)
    reboot.on_cancel(function(...)
        log:info("spdm has no extra cancel for reboot")
    end)
end

return app